6799
19503
Programmeringsspråkbøker forklarer at verdityper opprettes på bunken, og referansetyper opprettes på dyngen, uten å forklare hva disse to tingene er. Jeg har ikke lest en klar forklaring på dette. Jeg forstår hva en stabel er. Men,
Hvor og hva er de (fysisk i minnet på en ekte datamaskin)?
I hvilken grad kontrolleres de av operativsystemet eller språketid?
Hva er deres omfang?
Hva bestemmer størrelsen på hver av dem?
Hva gjør en raskere? 
Bunken er minnet som er avsatt som skrapeplass for en gjennomføringstråd. Når en funksjon kalles, reserveres en blokk på toppen av stabelen for lokale variabler og noe bokføringsdata. Når den funksjonen kommer tilbake, blir blokken ubrukt og kan brukes neste gang en funksjon kalles. Bunken er alltid reservert i en LIFO-rekkefølge (sist i først ut); den sist reservert blokken er alltid den neste blokken som blir frigjort. Dette gjør det veldig enkelt å holde styr på bunken; å frigjøre en blokk fra bunken er ikke annet enn å justere en peker.
Haugen er satt av til dynamisk tildeling. I motsetning til stakken, er det ikke noe håndhevet mønster for tildeling og deallokering av blokker fra haugen; du kan tildele en blokk når som helst og frigjøre den når som helst. Dette gjør det mye mer komplekst å holde oversikt over hvilke deler av dyngen som tildeles eller er gratis til enhver tid; det er mange egendefinerte dyngdeallokatorer tilgjengelig for å stille inn haugytelsen for forskjellige bruksmønstre.
Hver tråd får en bunke, mens det vanligvis bare er en bunke for applikasjonen (selv om det ikke er uvanlig å ha flere bunker for forskjellige typer tildelinger).
For å svare på spørsmålene dine direkte:
I hvilken grad kontrolleres de av operativsystemet eller språkets kjøretid?
OS tildeler stabelen for hver tråd på systemnivå når tråden opprettes. Vanligvis kalles operativsystemet av språketid for å tildele bunken til applikasjonen.
Hva er deres omfang?
Bunken er festet til en tråd, så når tråden går ut, blir stakken gjenvunnet. Bunken tildeles vanligvis ved oppstart av applikasjonen av kjøretiden, og gjenvinnes når applikasjonen (teknisk prosess) avsluttes.
Hva bestemmer størrelsen på hver av dem?
Størrelsen på stakken stilles inn når en tråd opprettes. Størrelsen på dyngen er satt ved oppstart av applikasjonen, men kan vokse etter hvert som det er behov for plass (tildeleren ber om mer minne fra operativsystemet).
Hva gjør en raskere?
Bunken er raskere fordi tilgangsmønsteret gjør det trivielt å tildele og distribuere minne fra den (en peker / heltall er ganske enkelt inkrementert eller dekrementert), mens haugen har mye mer kompleks bokføring involvert i en tildeling eller deallocation. Også, hver byte i bunken har en tendens til å bli brukt veldig ofte, noe som betyr at den har en tendens til å bli kartlagt til prosessorens cache, noe som gjør den veldig rask. En annen ytelseshit for bunken er at bunken, som for det meste er en global ressurs, vanligvis må være sikker på flere tråder, dvs. at hver tildeling og deallocation må - vanligvis - synkroniseres med "alle" andre haugetilganger i programmet.
En klar demonstrasjon:
Bildekilde: vikashazrati.wordpress.com
|
Stable:
Lagret i datamaskinens RAM akkurat som dyngen.
Variabler som er opprettet på bunken, kommer utenfor omfanget og blir automatisk distribuert.
Mye raskere å fordele i forhold til variabler på dyngen.
Implementert med en faktisk stabeldatastruktur.
Lagrer lokale data, returadresser, brukes til parameteroverføring.
Kan ha en stabeloverløp når for mye av stakken brukes (mest fra uendelig eller for dyp rekursjon, veldig store tildelinger).
Data opprettet på bunken kan brukes uten pekepinner.
Du vil bruke stakken hvis du vet nøyaktig hvor mye data du trenger å tildele før kompileringstid, og den ikke er for stor.
Har vanligvis en maksimal størrelse allerede bestemt når programmet starter.
Haug:
Lagret i datamaskinens RAM akkurat som bunken.
I C ++ må variabler på haugen ødelegges manuelt og aldri falle utenfor omfanget. Dataene frigjøres med sletting, sletting [] eller gratis.
Tregere å fordele i forhold til variabler på bunken.
Brukes på forespørsel for å tildele en blokk med data for bruk av programmet.
Kan ha fragmentering når det er mange tildelinger og deallokasjoner.
I C ++ eller C vil data opprettet på bunken bli pekt på av pekere og tildelt henholdsvis new eller malloc.
Kan ha tildelingsfeil hvis det blir bedt om å tildele for stor buffer.
Du vil bruke dyngen hvis du ikke vet nøyaktig hvor mye data du trenger på kjøretid, eller hvis du trenger å tildele mye data.
Ansvarlig for minnelekkasjer.
Eksempel:
int foo ()
{
røye * pBuffer; // <- ingenting tildelt ennå (unntatt selve pekeren, som er tildelt her på bunken).
bool b = sant; // Tildelt på bunken.
hvis (b)
{
// Opprett 500 byte på bunken
røyebuffer [500];
// Opprett 500 byte på dyngen
pBuffer = ny røye [500];
} // <- buffer er deallocated her, pBuffer is not
} // <--- oops det er en minnelekkasje, jeg burde ha kalt delete [] pBuffer;
|
Det viktigste poenget er at heap and stack er generiske begreper for måter minne kan tildeles på. De kan implementeres på mange forskjellige måter, og vilkårene gjelder for de grunnleggende konseptene.
I en bunke med gjenstander sitter varene oppå hverandre i den rekkefølgen de ble plassert der, og du kan bare fjerne den øverste(uten å velte det hele).
Enkelheten til en stabel er at du ikke trenger å vedlikeholde en tabell som inneholder en oversikt over hver del av allokert minne; den eneste tilstandsinformasjonen du trenger er en enkelt peker til slutten av stabelen. For å tildele og de-allokere, øker og reduserer du bare den ene pekeren. Merk: En bunke kan noen ganger implementeres for å starte på toppen av en del av minnet og strekke seg nedover i stedet for å vokse oppover.
I en haug er det ingen spesiell rekkefølge for måten ting plasseres på. Du kan nå og fjerne elementer i hvilken som helst rekkefølge fordi det ikke er noe tydelig "topp" element.
Haugetildeling krever å opprettholde en fullstendig oversikt over hvilket minne som tildeles og hva som ikke er, samt noe overhead vedlikehold for å redusere fragmentering, finne sammenhengende minnesegmenter som er store nok til å passe den ønskede størrelsen, og så videre. Minne kan tildeles når som helst og gir ledig plass. Noen ganger vil en minnetildeler utføre vedlikeholdsoppgaver som å defragmentere minne ved å flytte allokert minne rundt, eller søppeloppsamling - identifisere ved kjøretid når minnet ikke lenger er i omfang og distribuere det.
Disse bildene skal gjøre en ganske god jobb med å beskrive de to måtene å tildele og frigjøre minne i en bunke og en bunke. Nam!
I hvilken grad kontrolleres de av operativsystemet eller språkets kjøretid?
Som nevnt er heap og stack generelle vilkår, og kan implementeres på mange måter. Dataprogrammer har vanligvis en stabel som kalles en samtalestabel som lagrer informasjon som er relevant for den nåværende funksjonen, for eksempel en peker til hvilken funksjon den ble kalt fra, og eventuelle lokale variabler. Fordi funksjoner kaller andre funksjoner og deretter returnerer, vokser stabelen og krymper for å holde informasjon fra funksjonene lenger nede i samtalestakken. Et program har egentlig ikke kjøretidskontroll over det; det bestemmes av programmeringsspråket, operativsystemet og til og med systemarkitekturen.
En haug er et generelt begrep som brukes for ethvert minne som tildeles dynamisk og tilfeldig; dvs. ute av drift. Minnet tildeles vanligvis av operativsystemet, og applikasjonen ringer API-funksjoner for å gjøre denne tildelingen. Det er ganske mye overhead som kreves for å administrere dynamisk tildelt minne, som vanligvis håndteres av kjøretidskoden til programmeringsspråket eller miljøet som brukes.
Hva er deres omfang?
Samtalsstakken er et så lavt konsept at det ikke forholder seg til 'omfang' i form av programmering. Hvis du demonterer noen koder, vil du se relative referanser til pekerstilen til deler av bunken, men når det gjelder et høyere nivå språk, innfører språket sine egne regler for omfang. Et viktig aspekt ved en stabel er imidlertid at når en funksjon kommer tilbake, blir alt som er lokalt for den funksjonen umiddelbart frigjort fra stakken. Det fungerer slik du forventer at det skal fungere gitt hvordan programmeringsspråkene dine fungerer. I en haug er det også vanskelig å definere. Omfanget er hva som er utsatt av operativsystemet, men programmeringsspråket ditt legger sannsynligvis til sine regler om hva et "omfang" er i applikasjonen din. Prosessorarkitekturen og operativsystemet bruker virtuell adressering, som prosessoren oversetter til fysiske adresser og det er sidefeil osv. De holder rede på hvilke sider som tilhører hvilke applikasjoner. Du trenger aldri å bekymre deg for dette, skjønt, fordi du bare bruker hvilken metode programmeringsspråket ditt bruker for å tildele og frigjøre minne, og se etter feil (hvis tildelingen / frigjøringen mislykkes av en eller annen grunn).
Hva bestemmer størrelsen på hver av dem?
Igjen, det avhenger av språk, kompilator, operativsystem og arkitektur. En stabel er vanligvis forhåndsallokert, fordi den per definisjon må være sammenhengende minne. Språkkompilatoren eller operativsystemet bestemmer størrelsen. Du lagrer ikke store deler av data på bunken, så det vil være stort nok til at det aldri skal brukes fullt ut, bortsett fra i tilfeller av uønsket endeløs rekursjon (derav "stack overflow") eller andre uvanlige programmeringsbeslutninger.
En haug er et generelt begrep for alt som kan tildeles dynamisk. Avhengig av hvilken måte du ser på det, endrer det stadig størrelse. I moderne prosessorer og operativsystemer blir den eksakte måten den fungerer veldig abstrakt uansett, så du trenger normalt ikke å bekymre deg mye for hvordan den fungerer dypt nede, bortsett fra at (på språk der den lar deg) må du ikke bruke minne du ikke har tildelt ennå eller noe minne du har frigjort.
Hva gjør en raskere?
Bunken er raskere fordi alt ledig minne alltid er sammenhengende. Ingen liste trenger å opprettholdes over alle segmentene med ledig minne, bare en enkelt peker til gjeldende toppen av bunken. Kompilatorer lagrer vanligvis denne pekeren i et spesielt, raskt register for dette formålet. Dessuten er etterfølgende operasjoner på en bunke vanligvis konsentrert i meget nærliggende områder av minnet, noe som på et veldig lavt nivå er bra for optimalisering av prosessoren på døden.cacher.
|
(Jeg har flyttet dette svaret fra et annet spørsmål som var mer eller mindre en dupe av dette.)
Svaret på spørsmålet ditt er implementeringsspesifikt og kan variere mellom kompilatorer og prosessorarkitekturer. Her er imidlertid en forenklet forklaring.
Både stabelen og dyngen er minneområder som er tildelt fra det underliggende operativsystemet (ofte virtuelt minne som er kartlagt til fysisk minne på forespørsel).
I et miljø med flere tråder vil hver tråd ha sin egen helt uavhengige stabel, men de vil dele dyngen. Samtidig tilgang må kontrolleres på dyngen og er ikke mulig på bunken.
Haugen
Haugen inneholder en koblet liste over brukte og gratis blokker. Nye tildelinger på dyngen (av ny eller malloc) oppfylles ved å opprette en passende blokk fra en av gratisblokkene. Dette krever oppdatering av listen over blokker på dyngen. Denne metainformasjonen om blokkene på haugen lagres også på haugen ofte i et lite område rett foran hver blokk.
Etter hvert som dyngen vokser blir nye blokker ofte tildelt fra lavere adresser mot høyere adresser. Dermed kan du tenke på dyngen som en haug med minneblokker som vokser i størrelse når minnet blir tildelt. Hvis dyngen er for liten for en tildeling, kan størrelsen ofte økes ved å skaffe mer minne fra det underliggende operativsystemet.
Tildeling og distribusjon av mange små blokker kan etterlate haugen i en tilstand der det er mange små gratis blokker mellom de brukte blokkene. En forespørsel om å tildele en stor blokk kan mislykkes fordi ingen av de gratis blokkene er store nok til å tilfredsstille tildelingsforespørselen, selv om den kombinerte størrelsen på gratis blokkene kan være stor nok. Dette kalles haugfragmentering.
Når en brukt blokk som er tilstøtende til en fri blokk blir omplassert, kan den nye frie blokken slås sammen med den tilstøtende frie blokken for å skape en større fri blokk som effektivt reduserer fragmenteringen av dyngen.
Bunken
Stabelen fungerer ofte i tett lag med et spesialregister på CPU-en som heter stack-pekeren. Først peker pekepekeren mot toppen av bunken (den høyeste adressen på bunken).
CPU har spesielle instruksjoner for å skyve verdier på bunken og poppe dem tilbake fra bunken. Hvert trykk lagrer verdien på den nåværende plasseringen til stakkpekeren og reduserer stakkpekeren. En pop henter verdien som pekepekeren peker på, og øker deretter pekepekeren (ikke forveksles med det faktum at når du legger til en verdi i stabelen, reduseres pekepekeren og når du fjerner en verdi, øker den. bunnen). Verdiene som er lagret og hentet er verdiene til CPU-registrene.
Når en funksjon kalles, bruker CPU spesielle instruksjoner som skyver den nåværende instruksjonspekeren, dvs. adressen til koden som kjøres på bunken. CPUen hopper deretter til funksjonen ved å stille inn
instruksjonspeker til adressen til funksjonen som kalles. Senere, når funksjonen kommer tilbake, poppes den gamle instruksjonspekeren fra bunken, og kjøringen gjenopptas ved koden like etter samtalen til funksjonen.
Når en funksjon er angitt, reduseres stakkpekeren for å tildele mer plass på stakken for lokale (automatiske) variabler. Hvis funksjonen har en lokal 32 bit variabel, settes fire byte til side på bunken. Når funksjonen kommer tilbake, flyttes stabelpekeren tilbake for å frigjøre det tildelte området.
Hvis en funksjon har parametere, skyves disse på bunken før samtalen til funksjonen. Koden i funksjonen er da i stand til å navigere opp stabelen fra den nåværende stabelpekeren for å finne disse verdiene.
Nesting funksjon kaller fungerer som en sjarm. Hver nye samtale vil tildele funksjonsparametere, returadresse og plass til lokale variabler, og disse aktiveringspostene kan stables for nestede samtaler og vil slappe av på riktig måte når funksjonene kommer tilbake.
Siden stakken er en begrenset minneblokk, kan du forårsake en stabeloverflyt ved å ringe for mange nestede funksjoner og / eller tildele for mye plass til lokale variabler. Ofte er minneområdet som brukes til stakken satt opp på en slik måte at skriving under bunnen (den laveste adressen) til stakken vil utløse en felle eller unntak i CPUen. Denne eksepsjonelle tilstanden kan deretter fanges opp av kjøretiden og konverteres til et slags unntak for stackoverløp.
Kan en funksjon tildeles på bunken i stedet for en bunke?
Nei, aktiveringsposter for funksjoner (dvs. lokale eller automatiske variabler) er tildelt på stabelen som ikke bare brukes til å lagre disse variablene, men også for å holde oversikt over nestede funksjonsanrop.
Hvordan haugen administreres, er egentlig opp til kjøretidsmiljøet. C bruker malloc og C ++ bruker nytt, men mange andre språk har søppelinnsamling.
Imidlertid er stakken en mer lavt nivå-funksjon som er nært knyttet til prosessorarkitekturen. Å dyrke haugen når det ikke er nok plass, er ikke så vanskelig sidenden kan implementeres i bibliotekssamtalet som håndterer dyngen. Det er imidlertid ofte umulig å dyrke stabelen ettersom overløp av stabelen først oppdages når det er for sent; og å stenge tråden for henrettelse er det eneste levedyktige alternativet.
|
I den følgende C # -koden
offentlig ugyldig Metode1 ()
{
int i = 4;
int y = 2;
klasse1 cls1 = ny klasse1 ();
}
Slik administreres minnet
Lokale variabler som bare trenger å vare så lenge funksjonstegningen går i bunken. Haugen brukes til variabler hvis levetid vi egentlig ikke kjenner foran, men vi forventer at de varer en stund. På de fleste språk er det viktig at vi på kompileringstid vet hvor stor en variabel er hvis vi vil lagre den på bunken.
Objekter (som varierer i størrelse når vi oppdaterer dem) går på bunken fordi vi ikke vet når du oppretter hvor lenge de skal vare. På mange språk samles haugen for å finne gjenstander (for eksempel cls1-objektet) som ikke lenger har referanser.
I Java går de fleste objekter direkte inn i dyngen. På språk som C / C ++ kan strukturer og klasser ofte være på bunken når du ikke har med pekere å gjøre.
Mer informasjon finner du her:
Forskjellen mellom tildeling av stack og heapminne «timmurphy.org
og her:
Opprette objekter på stabelen og dyngen
Denne artikkelen er kilden til bildet ovenfor: Seks viktige .NET-konsepter: Stack, heap, verdityper, referansetyper, boksing og unboxing - CodeProject
men vær oppmerksom på at det kan inneholde unøyaktigheter.
|
Stakken
Når du kaller en funksjon legges argumentene til den funksjonen pluss noen andre overhead på bunken. Noe info (for eksempel hvor du skal dra tilbake) er også lagret der.
Når du erklærer en variabel i funksjonen din, tildeles den variabelen også på bunken.
Å omlokalisere bunken er ganske enkelt fordi du alltid omfordeler i omvendt rekkefølge du tildeler. Stabel ting legges til når du går inn i funksjoner, tilsvarende data blir fjernet når du avslutter dem. Dette betyr at du pleier å holde deg innenfor et lite område av bunken med mindre du ringer til mange funksjoner som kaller mange andre funksjoner (eller lager en rekursiv løsning).
Haugen
Bunken er et generisk navn for hvor du setter dataene du oppretter på farten. Hvis du ikke vet hvor mange romskip programmet ditt skal lage, vil du sannsynligvis bruke den nye (eller malloc eller tilsvarende) operatøren til å lage hvert romskip. Denne tildelingen kommer til å holde seg en stund, så det er sannsynlig at vi vil frigjøre ting i en annen rekkefølge enn vi opprettet dem.
Dermed er dyngen langt mer kompleks, fordi det til slutt blir minneregioner som er ubrukt sammenflettet med biter som er - hukommelsen blir fragmentert. Å finne ledig minne i størrelsen du trenger er et vanskelig problem. Dette er grunnen til at haugen bør unngås (selv om den fremdeles ofte brukes).
Gjennomføring
Implementering av både stabelen og dyngen er vanligvis nede på kjøretiden / operativsystemet. Ofte skaper spill og andre applikasjoner som er ytelseskritiske, egne minneløsninger som henter en stor mengde minne fra dyngen og deretter retter den ut internt for å unngå å stole på operativsystemet for minne.
Dette er bare praktisk hvis minnebruken din er ganske annerledes enn normen - det vil si for spill der du laster et nivå i en enorm operasjon og kan kaste hele veien i en annen stor operasjon.
Fysisk plassering i minnet
Dette er mindre relevant enn du tror på grunn av en teknologi som heter Virtual Memory, som får programmet til å tro at du har tilgang til en bestemt adresse der de fysiske dataene er et annet sted (selv på harddisken!). Adressene du får for stabelen er i økende rekkefølge etter hvert som ringetreet ditt blir dypere. Adressene til dyngen er uforutsigbare (dvs. implimenteringsspesifikke) og ærlig talt ikke viktige.
|
For å avklare har dette svaret feil informasjon (thomas fikset svaret etter kommentarer, kult :)). Andre svar unngår bare å forklare hva statisk tildeling betyr. Så jeg vil forklare de tre hovedformene for tildeling og hvordan de vanligvis forholder seg til haugen, stakken og datasegmentet nedenfor. Jeg vil også vise noen eksempler i både C / C ++ og Python for å hjelpe folk til å forstå.
"Statiske" (AKA statisk tildelte) variabler tildeles ikke på bunken. Ikke anta det - mange mennesker gjør bare fordi "statisk" høres ut som "stack". De eksisterer faktisk hverken i bunken eller dyngen. Dette er en del av det som kalles datasegmentet.
Imidlertid er det generelt bedre å vurdere "scope" og "lifetime" i stedet for "stack" og "heap".
Omfang refererer til hvilke deler av koden som har tilgang til en variabel. Generelt tenker vi på lokalt omfang (kan bare nås med den nåværende funksjonen) versus globalt omfang (kan nås hvor som helst) selv om omfanget kan bli mye mer komplekst.
Lifetime refererer til når en variabel tildeles og distribueres under programutførelsen. Vanligvis tenker vi på statisk tildeling (variabelvil vare gjennom hele programvarigheten, noe som gjør det nyttig for å lagre den samme informasjonen over flere funksjonsanrop) versus automatisk tildeling (variabel vedvarer bare under en enkelt samtale til en funksjon, noe som gjør den nyttig for lagring av informasjon som bare brukes under funksjon og kan kastes når du er ferdig) versus dynamisk tildeling (variabler hvis varighet er definert ved kjøretid, i stedet for kompileringstid som statisk eller automatisk).
Selv om de fleste kompilatorer og tolker implementerer denne oppførsel på samme måte når det gjelder bruk av stabler, dynger osv., Kan en kompilator noen ganger bryte disse konvensjonene hvis den vil så lenge oppførselen er korrekt. For eksempel, på grunn av optimalisering, kan en lokal variabel bare eksistere i et register eller fjernes helt, selv om de fleste lokale variabler finnes i stabelen. Som det er blitt påpekt i noen få kommentarer, er du fri til å implementere en kompilator som ikke en gang bruker en stabel eller en dyng, men i stedet noen andre lagringsmekanismer (sjelden gjort, siden stabler og dynger er bra for dette).
Jeg vil gi noen enkle merkede C-koder for å illustrere alt dette. Den beste måten å lære er å kjøre et program under en feilsøkingsprogram og se på oppførselen. Hvis du foretrekker å lese python, kan du hoppe til slutten av svaret :)
// Statisk tildelt i datasegmentet når programmet / DLL først lastes inn
// Omdelt når programmet / DLL avsluttes
// scope - kan nås hvor som helst i koden
int noenGlobalVariable;
// Statisk tildelt i datasegmentet når programmet først lastes inn
// Omdelt når programmet / DLL avsluttes
// scope - kan nås hvor som helst i denne bestemte kodefilen
statisk int someStaticVariable;
// "someArgument" tildeles på bunken hver gang MyFunction kalles
// "someArgument" blir distribuert når MyFunction returnerer
// scope - er kun tilgjengelig i MyFunction ()
ugyldig MyFunction (int someArgument) {
// Statisk tildelt i datasegmentet når programmet først lastes inn
// Omdelt når programmet / DLL avsluttes
// scope - er kun tilgjengelig i MyFunction ()
statisk int someLocalStaticVariable;
// Tildelt på bunken hver gang MyFunction kalles
// Omdelt når MyFunction kommer tilbake
// scope - er kun tilgjengelig i MyFunction ()
int someLocalVariable;
// En * peker * tildeles på bunken hver gang MyFunction kalles
// Denne pekeren blir fordelt når MyFunction returnerer
// scope - pekeren er kun tilgjengelig i MyFunction ()
int * someDynamicVariable;
// Denne linjen får plass til et heltall tildeles i dyngen
// når denne linjen kjøres. Merk at dette ikke er i begynnelsen av
// samtalen til MyFunction (), som de automatiske variablene
// scope - bare kode i MyFunction () har tilgang til dette rommet
// * gjennom denne variabelen *.
// Hvis du passerer adressen et annet sted, koden
// kan få tilgang til det også
someDynamicVariable = ny int;
// Denne linjen avdeler plass til heltallet i dyngen.
// Hvis vi ikke skrev det, ville minnet bli "lekket".
// Legg merke til en grunnleggende forskjell mellom stabelen og dyngen
// haugen må administreres. Bunken administreres for oss.
slett noeDynamicVariable;
// I andre tilfeller, i stedet for å distribuere dette dyngrommet du
// kan lagre adressen et sted som er mer permanent å bruke senere.
// Noen språk tar til og med hånd om plassering for deg ... men
// alltid må det tas vare på ved kjøretid av en eller annen mekanisme.
// Når funksjonen kommer tilbake, noenArgument, noeLokalVariabel
// og pekeren someDynamicVariable er fordelt.
// Rommet som noen DynamicVariable pekte på var allerede
// avtalt før retur.
komme tilbake;
}
// Merk at noeGlobalVariable, someStaticVariable og
// someLocalStaticVariable fortsetter å eksistere, og er ikke
// distribuert til programmet avsluttes.
Et spesielt gripende eksempel på hvorfor det er viktig å skille mellom levetid og omfang, er at en variabel kan ha lokalt omfang, men statisk levetid - for eksempel "someLocalStaticVariable" i kodeeksemplet ovenfor. Slike variabler kan gjøre våre vanlige, men uformelle navnevaner veldig forvirrende. For eksempel når vi sier "lokal" mener vi vanligvis "automatisk tildelt variabel lokalt", og når vi sier global, mener vi vanligvis "statisk allokert variabel med globalt omfang". Dessverre når det gjelder ting som "filavgrensede statisk tildelte variabler", sier mange bare ... "he ???".
Noen av syntaksvalgene i C / C ++ forverrer dette problemet - for eksempel tror mange at globale variabler ikke er "statiske" på grunn av syntaksen vist nedenfor.
int var1; // Har globalt omfang og statisk tildeling
statisk int var2; // Har filomfang og statisk tildeling
int main () {retur 0;}
Merk at å sette nøkkelordet "statisk" i erklæringen ovenfor hindrer var2 i å ha globalt omfang. Likevel har den globale var1 statisk tildeling. Dette ikke erintuitiv! Av denne grunn prøver jeg å aldri bruke ordet "statisk" når jeg beskriver omfang, og i stedet si noe som "fil" eller "filbegrenset" omfang. Imidlertid bruker mange uttrykket "statisk" eller "statisk omfang" for å beskrive en variabel som bare er tilgjengelig fra en kodefil. I livssammenheng betyr "statisk" alltid at variabelen tildeles ved programstart og deles når programmet avsluttes.
Noen mennesker tenker på disse konseptene som C / C ++ spesifikke. De er ikke. For eksempel illustrerer Python-eksemplet nedenfor alle tre typer tildeling (det er noen subtile forskjeller mulig i tolket språk som jeg ikke kommer inn på her).
fra datetime importer datetime
klasse Dyr:
_FavoriteFood = 'Udefinert' # _FavoriteFood er statisk tildelt
def PetAnimal (selv):
curTime = datetime.time (datetime.now ()) # curTime tildeles automatisk
skriv ut ("Takk for at du har klappet meg. Men det er" + str (curTime) + ", du bør gi meg mat. Min favorittmat er" + self._FavoriteFood)
klasse Katt (dyr):
_FavoriteFood = 'tunfisk' # Merk siden vi overstyrer, har Cat-klassen sin egen statisk tildelte _FavoriteFood-variabel, forskjellig fra dyrets
klasse Hund (dyr):
_FavoriteFood = 'biff' # På samme måte får hundeklassen sin egen statiske variabel. Viktig å merke seg - denne ene statiske variabelen deles mellom alle forekomster av hund, derfor er den ikke dynamisk!
hvis __name__ == "__main__":
kinnskjegg = Cat () # Dynamisk tildelt
fido = Dog () # Dynamisk tildelt
rinTinTin = Hund () # Dynamisk tildelt
kinnskjegg. PetAnimal ()
fido.PetAnimal ()
rinTinTin.PetAnimal ()
Dog._FavoriteFood = 'melkebein'
kinnskjegg. PetAnimal ()
fido.PetAnimal ()
rinTinTin.PetAnimal ()
# Output er:
# Takk for at du klappet meg. Men det er 13: 05: 02.255000, du burde gi meg mat. Min favorittmat er tunfisk
# Takk for at du klappet meg. Men det er 13: 05: 02.255000, du burde gi meg mat. Min favorittmat er biff
# Takk for at du klappet meg. Men det er 13: 05: 02.255000, du burde gi meg mat. Min favorittmat er biff
# Takk for at du klappet meg. Men det er 13: 05: 02.255000, du burde gi meg mat. Min favorittmat er tunfisk
# Takk for at du klappet meg. Men det er 13: 05: 02.255000, du burde gi meg mat. Min favorittmat er melkebein
# Takk for at du klappet meg. Men det er 13: 05: 02.256000, du burde gi meg mat. Min favorittmat er melkebein
|
Andre har svart på de brede strøk ganske bra, så jeg vil kaste inn noen detaljer.
Stack og haug trenger ikke være entall. En vanlig situasjon der du har mer enn en bunke er hvis du har mer enn en tråd i en prosess. I dette tilfellet har hver tråd sin egen stabel. Du kan også ha mer enn en bunke, for eksempel kan noen DLL-konfigurasjoner føre til at forskjellige DLLer tildeles fra forskjellige bunker, og det er derfor det generelt er en dårlig ide å frigjøre minne som er tildelt av et annet bibliotek.
I C kan du få fordelen med tildeling av variabel lengde gjennom bruk av alloca, som tildeles på bunken, i motsetning til tildeling, som tildeles på bunken. Dette minnet overlever ikke returoppgaven din, men den er nyttig for en skrapebuffer.
Å lage en enorm midlertidig buffer på Windows som du ikke bruker mye av, er ikke gratis. Dette er fordi kompilatoren vil generere en stack-probe-loop som kalles hver gang funksjonen din blir lagt inn for å sikre at stacken eksisterer (fordi Windows bruker en enkelt beskyttelsesside på slutten av stacken din for å oppdage når den må vokse stabelen. Hvis du får tilgang til minne mer enn én side fra slutten av bunken, vil du krasje). Eksempel:
ugyldig min funksjon ()
{
røye stor [10000000];
// Gjør noe som bare bruker de første 1K av store 99% av tiden.
}
|
Andre har direkte svart på spørsmålet ditt, men når jeg prøver å forstå stabelen og dyngen, tror jeg det er nyttig å vurdere minneoppsettet til en tradisjonell UNIX-prosess (uten tråder og mmap () -baserte allokatorer). Nettstedsiden Memory Management Glossary har et diagram over dette minneoppsettet.
Bunken og dyngen er tradisjonelt plassert i motsatte ender av prosessens virtuelle adresserom. Stabelen vokser automatisk når du får tilgang til den, opp til en størrelse som er satt av kjernen (som kan justeres med setrlimit (RLIMIT_STACK, ...)). Bunken vokser når minneallokatoren påkaller brk () eller sbrk () systemanrop, og kartlegger flere sider med fysisk minne i prosessens virtuelle adresserom.
I systemer uten virtuelt minne, som for eksempel noen innebygde systemer, gjelder ofte den samme grunnleggende utformingen, bortsett fra at stabelen og dyngen er faste. I andre innebygde systemer (for eksempel de som er basert på Microchip PIC-mikrokontrollere), er programstabelen en egen minneblokk som ikke kan adresseres av instruksjoner om dataflytting, og kan bare endres eller leses indirekte gjennom programflytinstruksjoner (ring retur osv.). Andre arkitekturer, som Intel Itanium-prosessorer, har flere stabler. I denne forstand er stakken et element i CPU-arkitekturen.
|
Bunken er en delminne som kan manipuleres via flere viktige monteringsspråkinstruksjoner, for eksempel 'pop' (fjern og returner en verdi fra stakken) og 'push' (skyv en verdi til stakken), men ring også (ring en underrutine - dette skyver adressen for å gå tilbake til stakken) og returnere (returnere fra en underrutine - dette spretter adressen ut av stabelen og hopper til den). Det er minneområdet under stakkpekerregisteret, som kan stilles inn etter behov. Stakken brukes også til å overføre argumenter til underrutiner, og også for å bevare verdiene i registre før du kaller til underrutiner.
Bunken er en del av minnet som blir gitt til et program av operativsystemet, vanligvis gjennom en syscall som malloc. På moderne operativsystemer er dette minnet et sett med sider som bare anropsprosessen har tilgang til.
Størrelsen på stakken bestemmes ved kjøretid, og vokser vanligvis ikke etter at programmet er startet. I et C-program må stakken være stor nok til å holde hver variabel som er deklarert i hver funksjon. Haugen vil vokse dynamisk etter behov, men operativsystemet gjør til slutt samtalen (det vil ofte vokse haugen med mer enn verdien som malloc krever, slik at i det minste noen fremtidige mallocs ikke trenger å gå tilbake til kjernen til få mer minne. Denne oppførselen kan ofte tilpasses)
Fordi du har tildelt stabelen før du starter programmet, trenger du aldri malloc før du kan bruke stabelen, så det er en liten fordel der. I praksis er det veldig vanskelig å forutsi hva som vil være raskt og hva som vil være sakte i moderne operativsystemer som har undersystemer for virtuelt minne, fordi hvordan sidene implementeres og hvor de lagres er en implementeringsdetalj.
|
Hva er en stabel?
En bunke er en haug med gjenstander, vanligvis en som er pent ordnet.
Stabler i databearkitekturer er regioner i minnet der data legges til eller fjernes på en siste-i-først-ut måte.
I en applikasjon med flere tråder vil hver tråd ha sin egen stabel.
Hva er en haug?
En haug er en uryddig samling av ting som er stablet opp tilfeldig.
I databearkitekturer er dyngen et område med dynamisk tildelt minne som administreres automatisk av operativsystemet eller minnebehandlingsbiblioteket.
Minne på dyngen tildeles, fordeles og endres størrelse regelmessig under programutførelse, og dette kan føre til et problem som kalles fragmentering.
Fragmentering oppstår når minneobjekter tildeles med små mellomrom som er for små til å holde ekstra minneobjekter.
Nettoresultatet er en prosentandel av haugeplassen som ikke kan brukes til videre minnetildeling.
Begge sammen
I en applikasjon med flere tråder vil hver tråd ha sin egen stabel. Men alle de forskjellige trådene vil dele haugen.
Fordi de forskjellige trådene deler haugen i en applikasjon med flere tråder, betyr dette også at det må være noe koordinering mellom trådene, slik at de ikke prøver å få tilgang til og manipulere det samme minnestykket i haugen samme tid.
Hvilken er raskere - stabelen eller dyngen? Og hvorfor?
Bunken er mye raskere enn dyngen.
Dette skyldes måten minnet tildeles på bunken.
Tildeling av minne på bunken er like enkelt som å flytte stakkpekeren opp.
For folk som er nye innen programmering, er det sannsynligvis en god ide å bruke stakken siden den er enklere.
Fordi stabelen er liten, vil du bruke den når du vet nøyaktig hvor mye minne du trenger for dataene dine, eller hvis du vet at størrelsen på dataene dine er veldig liten.
Det er bedre å bruke dyngen når du vet at du trenger mye minne for dataene dine, eller hvis du bare ikke er sikker på hvor mye minne du trenger (som med et dynamisk utvalg).
Java Memory Model
Stabelen er det minneområdet der lokale variabler (inkludert metodeparametere) lagres. Når det gjelder objektvariabler, er dette bare referanser (pekere) til de faktiske objektene på dyngen.
Hver gang et objekt blir instantiert, settes en mengde haugeminne til side for å holde dataene (tilstanden) til det objektet. Siden objekter kan inneholde andre objekter, kan noen av disse dataene faktisk inneholde referanser til de nestede objektene.
|
Jeg tror mange andre mennesker har gitt deg mest riktige svar på denne saken.
En detalj som har blitt savnet er imidlertid at "haugen" faktisk burde kalles "gratis butikk". Årsaken til dette skillet er at den opprinnelige gratisbutikken ble implementert med en datastruktur kjent som en "binomial haug." Av den grunn var tildeling fra tidlig implementering av malloc () / free () tildeling fra en haug. Imidlertid implementeres de fleste gratisbutikker i denne moderne tid med svært forseggjorte datastrukturer som ikke er binomale dynger.
|
Du kan gjøre noen interessante ting med stakken. For eksempel har du funksjoner som alloca (forutsatt at du kan komme forbi de store advarslene om bruken), som er en form for malloc sombruker spesielt stabelen, ikke bunken, til minne.
Når det er sagt, er stackbaserte minnefeil noe av det verste jeg har opplevd. Hvis du bruker heapminne, og du overskrider grensene for den tildelte blokken din, har du en anstendig sjanse for å utløse en segmentfeil. (Ikke 100%: Blokken din kan for øvrig være sammenhengende med en annen som du tidligere har tildelt.) Men siden variabler som er opprettet på bunken alltid er sammenhengende med hverandre, kan skriving utenfor grensene endre verdien til en annen variabel. Jeg har lært at når jeg føler at programmet mitt har sluttet å overholde logikkens lover, er det sannsynligvis bufferoverløp.
|
Enkelt, stabelen er der lokale variabler blir opprettet. Hver gang du ringer til en underrutine, blir programtelleren (pekeren til neste maskininstruksjon) og viktige registre, og noen ganger blir parameterne presset på bunken. Deretter skyves eventuelle lokale variabler inne i underrutinen på stabelen (og brukes derfra). Når underrutinen er ferdig, kommer alle tingene tilbake fra bunken. PC- og registerdataene kommer og settes tilbake der det var som de ble poppet, slik at programmet ditt kan fortsette sin glede.
Bunken er området for minne dynamiske minnetildelinger er laget av (eksplisitte "nye" eller "tildel" anrop). Det er en spesiell datastruktur som kan holde oversikt over minneblokker av forskjellige størrelser og deres tildelingsstatus.
I "klassiske" systemer ble RAM lagt ut slik at stakkpekeren startet på bunnen av minnet, heappekeren startet øverst, og de vokste mot hverandre. Hvis de overlapper hverandre, er du tom for RAM. Det fungerer ikke med moderne multi-threaded OSer skjønt. Hver tråd må ha sin egen stabel, og de kan opprettes dynamisk.
|
Fra WikiAnwser.
Stable
Når en funksjon eller en metode kaller en annen funksjon som i sin tur kaller en annen funksjon osv., Forblir utførelsen av alle disse funksjonene suspendert til den siste funksjonen returnerer sin verdi.
Denne kjeden av suspenderte funksjonsanrop er stakken, fordi elementene i stakken (funksjonsanrop) er avhengige av hverandre.
Stakken er viktig å ta med i unntakshåndtering og kjøring av tråder.
Haug
Haugen er ganske enkelt minnet som brukes av programmer for å lagre variabler.
Element av dyngen (variabler) har ingen avhengigheter med hverandre og kan alltid nås tilfeldig når som helst.
|
Stable
Veldig rask tilgang
Trenger ikke å eksplisitt distribuere variabler
Plass administreres effektivt av CPU, minne blir ikke fragmentert
Bare lokale variabler
Begrens på stabelstørrelse (OS-avhengig)
Variabler kan ikke endres
Haug
Variabler er tilgjengelige globalt
Ingen begrensning på minnestørrelse
(Relativt) tregere tilgang
Ingen garantert effektiv bruk av plass, hukommelse kan bli fragmentert over tid etter hvert som blokker av minne tildeles, og deretter frigjøres
Du må administrere minne (du har ansvaret for å tildele og frigjøre variabler)
Variabler kan endres med realloc ()
|
Kort oppsummert
En stabel brukes til statisk minnetildeling og en haug for dynamisk minnetildeling, begge lagret i datamaskinens RAM.
I detalj
Stakken
Stakken er en "LIFO" (siste inn, først ut) datastruktur, som styres og optimaliseres av CPU ganske tett. Hver gang en funksjon erklærer en ny variabel, "skyves" den på bunken. Hver gang en funksjon avsluttes, frigjøres alle variablene som skyves på stabelen av den funksjonen (det vil si at de blir slettet). Når en stackvariabel er frigjort, blir den regionen av minnet tilgjengelig for andre stackvariabler.
Fordelen med å bruke stakken til å lagre variabler, er at minne styres for deg. Du trenger ikke å tildele minne for hånd, eller frigjøre det når du ikke trenger det lenger. Dessuten, fordi CPU-en organiserer stackminne så effektivt, går det veldig raskt å lese fra og skrive til stack-variabler.
Mer finner du her.
Haugen
Bunken er en region i datamaskinens minne som ikke administreres automatisk for deg, og som ikke administreres like tett av CPUen. Det er en mer fritt flytende hukommelsesregion (og er større). For å tildele minne på dyngen, må du bruke malloc () eller calloc (), som er innebygde C-funksjoner. Når du har tildelt minne på dyngen, er du ansvarlig for å bruke gratis () til å fordele minnet når du ikke trenger det lenger.
Hvis du ikke klarer å gjøre dette, vil programmet ditt ha det som kalles minnelekkasje. Det vil si at minne på dyngen fortsatt vil bli satt til side (og vil ikke være tilgjengelig for andre prosesser). Som vi vil se i feilsøkingsdelen, er det et verktøy som heter Valgrind som kan hjelpe deg med å oppdage minnelekkasjer.
I motsetning til stakken har ikke haugen størrelsesbegrensninger på variabel størrelse (bortsett fra datamaskinens åpenbare fysiske begrensninger). Haugminne er litt tregere å lese fra og skrive til, fordi man må bruke pekere for å få tilgang til minne på haugen. Vi vil snakke om pekere om kort tid.
I motsetning til stakken,variabler opprettet på dyngen er tilgjengelige for alle funksjoner, hvor som helst i programmet ditt. Heapvariabler er i hovedsak globale i omfang.
Mer finner du her.
Variabler som er tildelt på bunken lagres direkte i minnet, og tilgangen til dette minnet er veldig rask, og tildelingen blir behandlet når programmet kompileres. Når en funksjon eller en metode kaller en annen funksjon som i sin tur kaller en annen funksjon osv., Forblir utførelsen av alle disse funksjonene suspendert til den siste funksjonen returnerer sin verdi. Bunken er alltid reservert i en LIFO-rekkefølge, den sist reservert blokken er alltid den neste blokken som skal frigjøres. Dette gjør det veldig enkelt å holde oversikt over stabelen. Å frigjøre en blokk fra stabelen er ikke noe mer enn å justere en peker.
Variabler som er tildelt på bunken, har sitt minne tildelt ved kjøretid, og tilgang til dette minnet er litt tregere, men bunnsstørrelsen er bare begrenset av størrelsen på det virtuelle minnet. Elementer av haugen har ingen avhengigheter med hverandre og kan alltid nås tilfeldig når som helst. Du kan tildele en blokk når som helst og frigjøre den når som helst. Dette gjør det mye mer komplekst å holde oversikt over hvilke deler av dyngen som tildeles eller er gratis til enhver tid.
Du kan bruke stakken hvis du vet nøyaktig hvor mye data du trenger å tildele før kompileringstid, og den ikke er for stor. Du kan bruke dyngen hvis du ikke vet nøyaktig hvor mye data du trenger ved kjøretid, eller hvis du trenger å tildele mye data.
I en situasjon med flere tråder vil hver tråd ha sin egen helt uavhengige stabel, men de vil dele dyngen. Stakken er trådspesifikk og bunken er applikasjonsspesifikk. Stakken er viktig å ta med i unntakshåndtering og kjøring av tråder.
Hver tråd får en bunke, mens det vanligvis bare er en bunke for applikasjonen (selv om det ikke er uvanlig å ha flere bunker for forskjellige typer tildelinger).
På applikasjonstid, hvis applikasjonen trenger mer dybde, kan den tildele minne fra ledig minne, og hvis stabelen trenger minne, kan den allokere minne fra ledig minne tildelt minne til applikasjonen.
Selv, mer detaljer er gitt her og her.
Nå kommer svaret til spørsmålet ditt.
I hvilken grad kontrolleres de av operativsystemet eller språkets kjøretid?
OS tildeler stabelen for hver tråd på systemnivå når tråden opprettes. Vanligvis kalles operativsystemet av språketid for å tildele bunken til applikasjonen.
Mer finner du her.
Hva er deres omfang?
Allerede gitt i toppen.
"Du kan bruke stakken hvis du vet nøyaktig hvor mye data du trenger å tildele før kompileringstid, og den ikke er for stor. Du kan bruke bunken hvis du ikke vet nøyaktig hvor mye data du trenger i løpetid eller hvis du må tildele mye data. "
Mer finner du her.
Hva bestemmer størrelsen på hver av dem?
Størrelsen på stakken er satt av OS når en tråd opprettes. Størrelsen på dyngen blir satt ved oppstart av applikasjonen, men den kan vokse etter hvert som det er behov for plass (tildeleren ber om mer minne fra operativsystemet).
Hva gjør en raskere?
Stabelallokering er mye raskere siden alt det egentlig er å flytte stabelpekeren. Ved å bruke minnepooler kan du få sammenlignbar ytelse ut av haugetildeling, men det kommer med litt ekstra kompleksitet og sin egen hodepine.
Også stack vs heap er ikke bare en ytelsesbetraktning; det forteller deg også mye om forventet levetid for objekter.
Detaljer finner du herfra.
|
OK, ganske enkelt og med korte ord, de betyr bestilt og ikke bestilt ...!
Stabel: I stabelelementer kommer ting oppå hverandre, betyr at de blir raskere og mer effektive å bli behandlet! ...
Så det er alltid en indeks for å peke på den spesifikke varen, også behandlingen blir raskere, det er også forhold mellom varene! ...
Heap: Ingen ordre, behandlingen vil bli tregere og verdiene blir rotet sammen uten noen spesifikk rekkefølge eller indeks ... det er tilfeldig og det er ingen sammenheng mellom dem ... så utførelse og brukstid kan variere ...
Jeg oppretter også bildet nedenfor for å vise hvordan de kan se ut:
|
stabling, heap og data for hver prosess i virtuelt minne:
|
På 1980-tallet forplantet UNIX seg som kaniner med store selskaper som ruller sine egne.
Exxon hadde en som hadde dusinvis av merkenavn tapt til historien.
Hvordan minnet ble lagt ut, var etter skjønnet til de mange implementatørene.
Et typisk C-program ble lagt ut flatt i minnet med
en mulighet til å øke ved å endre brk () -verdien.
Vanligvis var HEAP like under denne brk-verdien
og økende brk økte mengden tilgjengelig dyng.
Singelen STACK var vanligvis et område under HEAP som var et minneområde
inneholder ingenting av verdi før toppen av neste faste minneblokk.
Denne neste blokken var ofte CODE som kunne overskrives av stabeldata
i en av de berømte hackene fra sin tid.
En typisk minneblokk var BSS (en blokk på nullverdier)
som ved et uhell ikke ble nullstilt i en produsents tilbud.
En annen var DATA som inneholder initialiserte verdier, inkludert strenger og tall.
En tredje var CODE som inneholder CRT (C runtime), main, funksjoner og biblioteker.
Fremkomsten av virtuelt minne i UNIX endrer mange av begrensningene.
Det er ingen objektiv grunn til at disse blokkene trenger å være sammenhengende,
eller fast i størrelse, eller bestilt en bestemt måte nå.
Selvfølgelig, før UNIX var Multics som ikke led av disse begrensningene.
Her er et skjema som viser en av minnelayoutene i den tiden.
|
Et par cent: Jeg tror det vil være bra å tegne minne grafisk og mer enkelt:
Piler - viser hvor vokser stabelen og dyngen, prosessstakstørrelsen har grense, definert i OS, trådstakkestørrelsesgrensene av parametere i tråden oppretter API vanligvis. Heap begrenser vanligvis etter prosessens maksimale virtuelle minnestørrelse, for eksempel for 32 bit 2-4 GB.
Så enkel måte: prosessbunke er generelt for prosess og alle tråder inne, og brukes til minnetildeling i vanlige tilfeller med noe som malloc ().
Stack er hurtigminne for lagring av vanlige pekere og variabler for funksjonsretur, behandlet som parametere i funksjonsanrop, lokale funksjonsvariabler.
|
Siden noen svar ble nitpicking, skal jeg bidra med midd.
Overraskende nok har ingen nevnt at flere (dvs. ikke relatert til antall kjørende OS-nivå tråder) samtalestabler er å finne ikke bare på eksotiske språk (PostScript) eller plattformer (Intel Itanium), men også i fibre, grønne tråder og noen implementeringer av coroutines.
Fibre, grønne tråder og coroutines er på mange måter like, noe som fører til mye forvirring. Forskjellen mellom fibre og grønne tråder er at førstnevnte bruker kooperativ multitasking, mens sistnevnte kan inneholde enten samarbeidende eller forebyggende (eller til og med begge). For skillet mellom fibre og coroutines, se her.
I alle fall er formålet med både fibre, grønne tråder og koroutiner å ha flere funksjoner som utføres samtidig, men ikke parallelt (se dette SO-spørsmålet for skillet) innenfor en enkelt OS-nivå tråd, overføre kontroll frem og tilbake fra hverandre på en organisert måte.
Når du bruker fibre, grønne tråder eller koroutiner, har du vanligvis en egen stabel per funksjon. (Teknisk sett er ikke bare en stabel, men en hel kontekst av utførelse per funksjon. Viktigst, CPU registreres.) For hver tråd er det like mange stabler som det kjører funksjoner samtidig, og tråden bytter mellom å utføre hver funksjon i henhold til logikken til programmet ditt. Når en funksjon går til slutten, blir bunken ødelagt. Så antallet og levetiden til stabler er dynamiske og bestemmes ikke av antall tråder på OS-nivå!
Merk at jeg sa "har vanligvis en egen stabel per funksjon". Det er både stabile og stabile implementeringer av couroutines. De mest bemerkelsesverdige stabile C ++ - implementeringene er Boost.Coroutine og Microsoft PPLs async / wait. (Imidlertid vil C ++ 's gjenopptatte funksjoner (aka "async and wait"), som ble foreslått til C ++ 17, sannsynligvis bruke stabelløse coroutines.)
Fiberforslag til C ++ standardbiblioteket kommer. Det er også noen tredjepartsbiblioteker. Grønne tråder er ekstremt populære på språk som Python og Ruby.
|
Jeg har noe å dele, selv om hovedpoengene allerede er dekket.
Stable
Veldig rask tilgang.
Lagret i RAM.
Her lastes funksjonsanrop sammen med de lokale variablene og funksjonsparametrene som er sendt.
Plass frigjøres automatisk når programmet går utenfor et omfang.
Lagret i sekvensielt minne.
Haug
Langsom tilgang relativt til Stack.
Lagret i RAM.
Her lagres dynamisk opprettet variabler, som senere krever frigjøring av det tildelte minnet etter bruk.
Lagres hvor minnetildeling gjøres, alltid tilgjengelig av pekeren.
Interessant merknad:
Hvis funksjonssamtalene hadde blitt lagret i bunke, ville det ha resultert i to rotete poeng:
På grunn av sekvensiell lagring i stabelen er kjøringen raskere. Lagring i haug ville ha resultert i enormt tidsforbruk og dermed gjort at hele programmet ble tregere.
Hvis funksjoner ble lagret i heap (rotete lagring pekt av pekeren), ville det ikke vært noen måte å gå tilbake til innringeradressen (som stakken gir på grunn av sekvensiell lagring i minnet).
|
Wow! Så mange svar, og jeg tror ikke en av dem fikk det riktig ...
1) Hvor og hva er de (fysisk i minnet på en ekte datamaskin)?
Bunken er minne som begynner som den høyeste minneadressen som er tildelt programbildet ditt, og deretter reduseres verdien derfra. Den er reservert for kalt funksjonsparametere og for alle midlertidige variabler som brukes i funksjoner.
Det er to dynger: offentlige og private.
Den private bunken begynner på en 16-byte-grense (for 64-biters programmer) eller en 8-byte-grense (for 32-biters programmer) etter den siste byten med kode i programmet ditt, og øker deretter iverdi derfra. Det kalles også standardbunken.
Hvis den private dyngen blir for stor, vil den overlappe stabelområdet, og den vil også overlappe bunken hvis den blir for stor. Fordi stabelen starter på en høyere adresse og jobber seg ned til lavere adresse, kan du med riktig hacking gjøre stakken så stor at den vil overkjøre det private haugområdet og overlappe kodeområdet. Trikset er da å overlappe nok av kodeområdet til at du kan koble til koden. Det er litt vanskelig å gjøre, og du risikerer et programkrasj, men det er enkelt og veldig effektivt.
Den offentlige bunken ligger i sin egen minneplass utenfor programbildeplassen. Det er dette minnet som blir overført til harddisken hvis minnesressursene blir knappe.
2) I hvilken grad kontrolleres de av operativsystemet eller språkets kjøretid?
Bunken styres av programmereren, den private bunken styres av operativsystemet, og den offentlige bunken styres ikke av noen fordi det er en OS-tjeneste - du kommer med forespørsler og enten blir de gitt eller nektet.
2b) Hva er deres omfang?
De er alle globale for programmet, men innholdet kan være privat, offentlig eller globalt.
2c) Hva bestemmer størrelsen på hver av dem?
Størrelsen på stakken og den private bunken bestemmes av kompilatorens kjøretidsalternativer. Den offentlige bunken initialiseres ved kjøretid ved hjelp av en størrelsesparameter.
2d) Hva gjør en raskere?
De er ikke designet for å være raske, de er designet for å være nyttige. Hvordan programmereren bruker dem avgjør om de er "raske" eller "sakte"
REF:
https://norasandler.com/2019/02/18/Write-a-Compiler-10.html
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate
|
Mange svar er riktige som begreper, men vi må merke oss at en stabel er nødvendig av maskinvaren (dvs. mikroprosessor) for å tillate anrop til underrutiner (CALL på monteringsspråk ..). (OOP-gutta vil kalle det metoder)
På bunken lagrer du returadresser og ring → push / ret → pop administreres direkte i maskinvare.
Du kan bruke stakken til å overføre parametere .. selv om den er tregere enn å bruke registre (vil en mikroprosessorguru si eller en god BIOS-bok fra 1980-tallet ...)
Uten stabel kan ingen mikroprosessor fungere. (vi kan ikke forestille oss et program, selv i monteringsspråk, uten underrutiner / funksjoner)
Uten haugen kan det. (Et monteringsspråkprogram kan fungere uten, da bunken er et OS-konsept, som malloc, det vil si et OS / Lib-anrop.
Stabelbruk er raskere som:
Er maskinvare, og til og med push / pop er veldig effektive.
malloc krever at du går inn i kjernemodus, bruker lås / semafor (eller andre synkroniseringsprimitiver) som utfører kode og administrerer noen strukturer som trengs for å holde orden på allokering.
|
Heap er et område med dynamisk tildelt minne som administreres automatisk av operativsystemet eller Memory Manager-biblioteket. Du kan tildele en blokk når som helst og frigjøre den når som helst. Haugetildeling krever å opprettholde en full oversikt over hvilket minne som tildeles og hva som ikke er, samt noe overheadvedlikehold for å redusere fragmentering, finne sammenhengende minnesegmenter som er store nok til å passe den ønskede størrelsen, og så videre. Minne kan tildeles når som helst og gir ledig plass. Etter hvert som dyngen vokser blir nye blokker ofte tildelt fra lavere adresser mot høyere adresser. Dermed kan du tenke på dyngen som en haug med minneblokker som vokser i størrelse når minnet blir tildelt. Hvis dyngen er for liten for en tildeling, kan størrelsen ofte økes ved å skaffe mer minne fra det underliggende operativsystemet. Minne som er tildelt fra dyngen vil forbli allokert til ett av følgende skjer:
Minnet er frigjort
Programmet avsluttes
Stable:
Lagret i datamaskinens RAM akkurat som dyngen.
Variabler som er opprettet på bunken, kommer utenfor omfanget og blir automatisk distribuert.
Mye raskere å fordele i forhold til variabler på dyngen.
Lagrer lokale data, returadresser, brukes til parameteroverføring.
Kan ha en stabeloverløp når for mye av stakken brukes (for det meste
fra uendelig eller for dyp rekursjon, veldig store tildelinger).
Du vil bruke stakken hvis du vet nøyaktig hvor mye data du trenger
tildeler før kompileringstid, og den er ikke for stor.
Har vanligvis en maksimal størrelse allerede bestemt når programmet ditt
starter.
Haug:
Lagret i datamaskinens RAM akkurat som bunken.
I C ++ må variabler på haugen ødelegges manuelt og aldri
faller utenfor omfanget.
Dataene frigjøres med sletting, sletting [] eller gratis.
Tregere å fordele i forhold til variabler på bunken.
Brukes på forespørsel for å tildele en blokk med data for bruk av programmet.
Kan ha fragmentering når det er mye tildelinger og
deallokasjoner.
I C ++ eller C blir data som er opprettet på dyngen pekt på av pekere
og tildelt henholdsvis ny eller malloc.
Kan ha tildelingsfeil hvis det blir bedt om for stor buffer
tildeles.
Dubruker haugen hvis du ikke vet nøyaktig hvor mye data du har
trenger på kjøretid eller hvis du trenger å tildele mye data.
Ansvarlig for minnelekkasjer.
|
Bunken er egentlig et lett tilgjengelig minne som bare administrerer elementene
som en - vel - stabel. Bare gjenstander som størrelsen er kjent på forhånd kan gå på bunken. Dette er tilfelle for tall, strenger, booleanere.
Haugen er et minne for gjenstander som du ikke kan forhåndsbestemme
nøyaktig størrelse og struktur. Siden objekter og matriser kan muteres og
endring ved kjøretid, må de gå inn i dyngen.
Kilde: Academind
|
CPU-stack og heap er fysisk relatert til hvordan CPU og register fungerer med minne, hvordan maskinmonteringsspråk fungerer, ikke språk på høyt nivå selv, selv om disse språkene kan bestemme små ting.
Alle moderne prosessorer arbeider med den "samme" mikroprosessorteorien: de er alle basert på det som kalles "registre", og noen er for "stack" for å oppnå ytelse. Alle prosessorer har stabelregister siden begynnelsen, og de hadde alltid vært her, som jeg vet. Monteringsspråk er de samme siden begynnelsen, til tross for variasjoner ... opp til Microsoft og dets mellomliggende språk (IL) som endret paradigmet til å ha et OO-språk for samling av virtuelle maskiner. Så vi vil kunne ha noen CLI / CIL CPU i fremtiden (ett prosjekt av MS).
CPUer har stabelregistre for å øke hastigheten på minnetilgang, men de er begrenset sammenlignet med bruk av andre registre for å få full tilgang til alt tilgjengelig minne for prosessen. Det var derfor vi snakket om tildeling av stabler og dynger.
Oppsummert, og generelt, er bunken hudge og treg og er for "globale" forekomster og objekter, siden stakken er liten og rask og for "lokale" variabler og referanser (skjulte pekere å glemme å administrere dem).
Så når vi bruker det nye nøkkelordet i en metode, blir referansen (en int) opprettet i bunken, men objektet og alt innholdet (verdityper så vel som objekter) blir opprettet i bunken, hvis jeg husker det. Men lokale elementære verdityper og matriser opprettes i stabelen.
Forskjellen i minnetilgang er på cellenes referansenivå: adressering av dyngen, det totale minnet i prosessen, krever mer kompleksitet når det gjelder håndtering av CPU-registre, enn stakken som er "mer" lokalt når det gjelder adressering fordi CPU-stakken register brukes som basisadresse, hvis jeg husker det.
Det er grunnen til at når vi har veldig lange eller uendelige ringeanrop eller løkker, fikk vi raskt overløp uten å fryse systemet på moderne datamaskiner ...
C # Heap (ing) Vs Stack (ing) I .NET
Stack vs Heap: Know the Difference
Statisk klasseminnetildeling der den er lagret C #
Hva og hvor er stabelen og dyngen?
https://en.wikipedia.org/wiki/Memory_management
https://en.wikipedia.org/wiki/Stack_register
Samlingsspråkressurser:
Programmeringsveiledning for montasje
Intel® 64 og IA-32 Architectures Software Developer Manuals
|
Takk for en virkelig god diskusjon, men som en virkelig noob lurer jeg på hvor instruksjonene holdes? I BEGJENNINGEN bestemte forskere mellom to arkitekturer (von NEUMANN hvor alt regnes som DATA og HARVARD hvor et område av minne var reservert for instruksjoner og et annet for data). Til syvende og sist gikk vi med von Neumann-designet, og nå regnes alt som det samme. Dette gjorde det vanskelig for meg da jeg lærte montering
https://www.cs.virginia.edu/~evans/cs216/guides/x86.html
fordi de snakker om registre og stakk pekere.
Alt ovenfor snakker om DATA. Min gjetning er at siden en instruksjon er en definert ting med et spesifikt minnefotavtrykk, vil den gå på bunken, og så alle 'de' registerene som er diskutert i montering, er på bunken. Selvfølgelig kom objektorientert programmering med instruksjoner og data som kom inn i en struktur som var dynamisk, så nå ville instruksjonene også bli holdt på dyngen?
|
Svært aktivt spørsmål. Tjen 10 rykte for å svare på dette spørsmålet. Omdømmekravet hjelper deg med å beskytte dette spørsmålet mot spam og ikke-svar-aktivitet.
Er ikke svaret du leter etter? Bla gjennom andre spørsmål merket minnehåndteringsstabel språk-agnostisk haug dynamisk-minne-tildeling, eller still ditt eget spørsmål.